package com.knowvoyage.knowvoyageserver.service.Impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.knowvoyage.knowvoyageserver.mapper.*;
import com.knowvoyage.knowvoyageserver.pojo.entity.ErrorBook;
import com.knowvoyage.knowvoyageserver.pojo.entity.ErrorRecord;
import com.knowvoyage.knowvoyageserver.pojo.vo.ErrorRecordVO;
import com.knowvoyage.knowvoyageserver.pojo.vo.Result;
import com.knowvoyage.knowvoyageserver.service.ErrorBookService;
import jakarta.validation.constraints.Null;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class ErrorBookServiceImpl implements ErrorBookService {
    private final ErrorBookMapper errorBookMapper;
    private final ErrorRecordMapper errorRecordMapper;
    private final QuestionMapper  questionMapper;

    private final RedisTemplate<String, Object> redisTemplate;  // 注入 RedisTemplate

    // 缓存键前缀
    private static final String EBBING_CACHE_PREFIX = "ebbing:book:";
    private static final String RECORD_CACHE_PREFIX = "record:user:";
    private static final String BOOK_CACHE_PREFIX = "book:user:";
    private static final String RECORD_LIST_CACHE_PREFIX = "recordlist:book:"; // 错题列表缓存


    // 序列化工具
    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 新增错题本
     *
     * @param errorBook 错题本记录对象
     * @return 新增结果
     */
    @Override
    public Result<Boolean> insert(ErrorBook errorBook) {
        // 1. 参数校验
        if (errorBook == null) {
            log.error("新增错题本记录失败：参数不能为null");
            return Result.error(400, "参数不能为空");
        }

        try {
            // 2. 业务校验（示例，根据实际需求调整）
            if (errorBook.getUserId() == null) {
                return Result.error(400, "用户ID不能为空");
            }
            errorBookMapper.insert(errorBook);
            log.info("尝试插入errorBookId：{}", errorBook.getBookId());
        } catch (Exception e) {
            log.error("新增错题本记录发生未知错误", e);
            return Result.error(500, "系统内部错误");
        }
        String pattern = BOOK_CACHE_PREFIX + errorBook.getUserId();
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
        return Result.success(true);
    }

    /**
     * 根据用户ID查询错题本
     *
     * @param userId 用户ID
     * @return 错题本列表
     */
    @Override
    public Result<List<ErrorBook>> selectByUserId(Long userId) {
        // 参数校验
        if (userId == null) {
            return Result.error(400, "用户ID不能为空");
        }

        // 1. 构建缓存键
        String cacheKey = BOOK_CACHE_PREFIX + userId;

        // 2. 尝试从Redis获取缓存
        try {
            String cachedData = (String) redisTemplate.opsForValue().get(cacheKey);
            if (cachedData != null) {
                List<ErrorBook> cachedList = objectMapper.readValue(cachedData,
                        new TypeReference<List<ErrorBook>>() {});
                log.info("从缓存中获取数据成功，数据为：{}", cachedList);
                return Result.success(cachedList);
            }
        } catch (Exception e) {
            log.error("Redis缓存读取失败，继续查询数据库", e);
        }

        log.info("缓存未命中，开始查询数据库...");
        // 3. 缓存未命中，查询数据库
        List<ErrorBook> errorBooks = errorBookMapper.selectByUserId(userId);

        // 4. 处理空结果
        if (errorBooks == null || errorBooks.isEmpty()) {
            log.info("用户ID：{}，未找到对应的错题本记录", userId);
            return Result.success(Collections.emptyList());
        }
        for (ErrorBook errorBook : errorBooks){
            errorBook.setCreateTime(null);
        }

        // 5. 写入Redis缓存
        try {
            String jsonData = objectMapper.writeValueAsString(errorBooks);
            redisTemplate.opsForValue().set(
                    cacheKey,
                    jsonData,
                    1, // 过期时间（小时）
                    TimeUnit.HOURS
            );
        } catch (Exception e) {
            log.error("Redis缓存写入失败", e);
        }

        return Result.success(errorBooks);
    }

    /**
     * 删除错题本
     *
     * @param bookId    错题本ID
     * @return 删除结果
     */
    @Override
    public Result<Boolean> delete(Long bookId) {
        // 1. 先查找该错题本记录是否存在
        ErrorBook errorBook = errorBookMapper.selectById(bookId);
        if (errorBook == null) {
            log.warn("删除失败，bookId={} 不存在", bookId);
            return Result.error(404, "要删除的错题本记录不存在");
        }
        // 2. 删除数据库记录
        errorBookMapper.delete(bookId);
        // 3. 删除缓存
        Long userId = errorBook.getUserId();
        String pattern = BOOK_CACHE_PREFIX + userId + "*";
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }

        return Result.success(true);
    }


    /**
     * 更新错题本
     *
     * @param errorBook 错题本对象
     * @return 更新结果
     */
    @Override
    public Result<Boolean> update(ErrorBook errorBook) {
        // 1. 查询原始数据，确认 bookId 是否存在
        ErrorBook existing = errorBookMapper.selectById(errorBook.getBookId());
        if (existing == null) {
            return Result.error(404, "要更新的错题本记录不存在");
        }

        // 2. 执行更新
        errorBookMapper.update(errorBook);

        // 3. 删除 Redis 缓存
        Long userId = existing.getUserId();
        String pattern = BOOK_CACHE_PREFIX + userId + "*"; // 加 * 确保模糊匹配
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }

        return Result.success(true);
    }


    /**
     * 分页查询用户下的错题本
     *
     * @param userId    用户ID
     * @param pageNum   页码
     * @param pageSize  每页大小
     * @return 分页结果
     */
    @Override
    public PageInfo<ErrorBook> selectAllByUser(Long userId, int pageNum, int pageSize) {
        return null;
    }

    /**
     * 根据错题本ID查询所有记录
     *
     * @param bookId    错题本ID
     * @param pageNum   页码
     * @param pageSize  每页大小
     * @return 分页结果
     */
    @Override
    public PageInfo<ErrorRecordVO> selectAllRecordByBookId(Long bookId, int pageNum, int pageSize) {
        // 0. 参数校验
        if (bookId == null || pageNum <= 0 || pageSize <= 0) {
            log.error("查询错题列表参数错误");
            return new PageInfo<>();
        }

        // 1. 构造缓存键（可包含分页信息）
        String cacheKey = RECORD_LIST_CACHE_PREFIX + bookId + ":page:" + pageNum + ":size:" + pageSize;

        // 2. 尝试从缓存获取
        try {
            String cachedJson = (String) redisTemplate.opsForValue().get(cacheKey);
            if (cachedJson != null) {
                List<ErrorRecordVO> cachedList = objectMapper.readValue(cachedJson, new TypeReference<List<ErrorRecordVO>>() {});
                log.info("错题列表命中缓存：{}", cacheKey);
                return PageInfo.of(cachedList); // 不分页了，仅封装数据列表（PageInfo本身是包装器）
            }
        } catch (Exception e) {
            log.error("读取错题缓存失败，继续查库", e);
        }
        log.info("缓存未命中，开始查询数据库...");
        // 3. 查询数据库
        PageHelper.startPage(pageNum, pageSize);
        List<ErrorRecordVO> errorRecords = errorRecordMapper.selectByBook(bookId);
        PageInfo<ErrorRecordVO> pageInfo = PageInfo.of(errorRecords);

        // 4. 写入缓存（异步或try-catch保护）
        try {
            String jsonData = objectMapper.writeValueAsString(errorRecords);
            redisTemplate.opsForValue().set(
                    cacheKey,
                    jsonData,
                    1, // 过期时间
                    TimeUnit.HOURS
            );
        } catch (Exception e) {
            log.error("写入错题缓存失败", e);
        }

        return pageInfo;
    }


    /**
     * 根据错题本ID查询艾宾浩斯需要复习的记录
     *
     * @param bookId    错题本ID
     * @param pageNum   页码
     * @param pageSize  每页大小
     * @return 分页结果
     */
    @Override
    public PageInfo<ErrorRecordVO> selectEbbingRecordByBookId(Long bookId, int pageNum, int pageSize) {
        if (bookId == null || pageNum <= 0 || pageSize <= 0) {
            log.error("查询艾宾浩斯错题参数错误");
            return new PageInfo<>();
        }

        String cacheKey = EBBING_CACHE_PREFIX + bookId + ":page:" + pageNum + ":size:" + pageSize;

        try {
            String cachedJson = (String) redisTemplate.opsForValue().get(cacheKey);
            if (cachedJson != null) {
                List<ErrorRecordVO> cachedList = objectMapper.readValue(cachedJson, new TypeReference<List<ErrorRecordVO>>() {});
                log.info("艾宾浩斯缓存命中：{}", cacheKey);
                return PageInfo.of(cachedList);
            }
        } catch (Exception e) {
            log.error("艾宾浩斯缓存读取失败，转查数据库", e);
        }

        log.info("艾宾浩斯缓存未命中，查询数据库...");
        PageHelper.startPage(pageNum, pageSize);
        List<ErrorRecordVO> errorRecords = errorRecordMapper.selectEbbingByBook(bookId, Arrays.asList(1L, 4L, 7L, 15L));
        PageInfo<ErrorRecordVO> pageInfo = PageInfo.of(errorRecords);

        try {
            String jsonData = objectMapper.writeValueAsString(errorRecords);
            redisTemplate.opsForValue().set(cacheKey, jsonData, 1, TimeUnit.HOURS);
        } catch (Exception e) {
            log.error("艾宾浩斯缓存写入失败", e);
        }

        return pageInfo;
    }


    @Override
    public Result<List<ErrorBook>> selectAll() {
        return null;
    }

    /**
     * 添加错题
     *
     * @param errorRecord 错题对象
     * @return 添加结果
     */
    @Override
    public Result<Boolean> insertRecord(ErrorRecord errorRecord) {
        if (errorBookMapper.selectById(errorRecord.getBookId()) == null) {
            return Result.error(400, "该错题本不存在");
        }
        //是否已存在此错题
        ErrorRecord errRecord = errorRecordMapper.selectByBookAndQuestion(errorRecord.getBookId(), errorRecord.getQuestionId());
        if (errRecord != null){
            return Result.error(400, "该错题已存在");
        }
        try {
            errorRecordMapper.insert(errorRecord);
        } catch (DuplicateKeyException e) {
            return Result.error(400, "该错题已存在");
        }

        // 清除缓存
        String pattern = RECORD_LIST_CACHE_PREFIX + errorRecord.getBookId() + ":*";
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
        String ebbingPattern = EBBING_CACHE_PREFIX + errorRecord.getBookId() + ":*";
        Set<String> ebbingKeys = redisTemplate.keys(ebbingPattern);
        if (ebbingKeys != null && !ebbingKeys.isEmpty()) {
            redisTemplate.delete(ebbingKeys);
        }


        return Result.success(true);
    }


    /**
     * 删除错题
     *
     * @param recordId 错题ID
     * @return 删除结果
     */
    @Override
    public Result<Boolean> deleteRecord(Long recordId) {
        if (recordId == null || recordId <= 0) {
            return Result.error(400, "参数错误");
        }

        ErrorRecord record = errorRecordMapper.selectById(recordId);
        if (record != null) {
            log.info("删除错题：{}", record);
            // 清除普通错题记录缓存
            String pattern = RECORD_LIST_CACHE_PREFIX + record.getBookId() + ":*";
            Set<String> keys = redisTemplate.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
            }

            // 清除艾宾浩斯缓存
            String ebbingPattern = EBBING_CACHE_PREFIX + record.getBookId() + ":*";
            Set<String> ebbingKeys = redisTemplate.keys(ebbingPattern);
            if (ebbingKeys != null && !ebbingKeys.isEmpty()) {
                redisTemplate.delete(ebbingKeys);
            }
        }

        errorRecordMapper.delete(recordId);
        return Result.success(true);
    }

    @Override
    public Result<Boolean> insertRecordsBatch(List<ErrorRecord> records, Long userId) {
        if (records == null || records.isEmpty()) {
            return Result.error(400, "参数不能为空");
        }

        // 1. 先批量获取所有涉及的错题本ID，确认都属于该userId
        Set<Long> bookIds = new HashSet<>();
        for (ErrorRecord record : records) {
            bookIds.add(record.getBookId());
        }
        // 查询这些错题本
        List<ErrorBook> errorBooks = errorBookMapper.selectByIds(new ArrayList<>(bookIds));
        log.info("批量插入错题，共{}个错题本", errorBooks);
        if (errorBooks.size() != bookIds.size()) {
            return Result.error(400, "存在无效的错题本ID");
        }
        // 校验所有错题本均属于当前用户
        for (ErrorBook book : errorBooks) {
            if (!userId.equals(book.getUserId())) {
                return Result.error(403, "存在非当前用户的错题本，禁止操作");
            }
        }

        // 2. 遍历批量插入，跳过重复错题
        int insertedCount = 0;
        for (ErrorRecord record : records) {
            // 检查此题是否已存在
            ErrorRecord exist = errorRecordMapper.selectByBookAndQuestion(record.getBookId(), record.getQuestionId());
            if (exist == null) {
                try {
                    errorRecordMapper.insert(record);
                    insertedCount++;
                } catch (DuplicateKeyException e) {
                    // 如果并发导致重复，忽略
                    log.warn("插入重复错题被忽略 bookId={} questionId={}", record.getBookId(), record.getQuestionId());
                } catch (Exception e) {
                    log.error("批量插入错题异常", e);
                    return Result.error(500, "系统错误");
                }
            }
        }

        // 3. 清除所有涉及错题本相关缓存
        for (Long bookId : bookIds) {
            clearCacheByBookId(bookId);
        }

        return Result.success(true);
    }

    @Override
    public Result<Boolean> deleteRecordsBatch(List<Long> recordIds, Long userId) {

        if (recordIds == null || recordIds.isEmpty()) {
            return Result.error(400, "参数不能为空");
        }

        // 1. 查询所有待删除错题
        List<ErrorRecord> records = errorRecordMapper.selectByIds(recordIds);
        if (records.size() != recordIds.size()) {
            return Result.error(400, "存在无效的错题记录ID");
        }

        // 2. 校验所有错题所属错题本是否属于该用户
        Set<Long> bookIds = new HashSet<>();
        for (ErrorRecord record : records) {
            bookIds.add(record.getBookId());
        }
        List<ErrorBook> errorBooks = errorBookMapper.selectByIds(new ArrayList<>(bookIds));
        log.info("批量删除错题，共{}个错题本", errorBooks);
        Map<Long, Long> bookIdToUserId = new HashMap<>();
        for (ErrorBook book : errorBooks) {
            bookIdToUserId.put(book.getBookId(), book.getUserId());
        }
        for (ErrorRecord record : records) {
            Long bookUserId = bookIdToUserId.get(record.getBookId());
            if (bookUserId == null || !bookUserId.equals(userId)) {
                return Result.error(403, "存在非当前用户的错题记录，禁止操作");
            }
        }

        // 3. 批量删除
        try {
            errorRecordMapper.deleteBatch(recordIds);
        }
        catch (Exception e) {
            log.error("批量删除错题异常", e);
            return Result.error(500, "系统错误");
        }

        // 4. 清除缓存
        for (Long bookId : bookIds) {
            clearCacheByBookId(bookId);
        }

        return Result.success(true);
    }

    // 清理该错题本相关缓存（错题列表和艾宾浩斯复习缓存）
    private void clearCacheByBookId(Long bookId) {
        String recordListPattern = RECORD_LIST_CACHE_PREFIX + bookId + ":*";
        Set<String> recordKeys = redisTemplate.keys(recordListPattern);
        if (recordKeys != null && !recordKeys.isEmpty()) {
            redisTemplate.delete(recordKeys);
        }
        String ebbingPattern = EBBING_CACHE_PREFIX + bookId + ":*";
        Set<String> ebbingKeys = redisTemplate.keys(ebbingPattern);
        if (ebbingKeys != null && !ebbingKeys.isEmpty()) {
            redisTemplate.delete(ebbingKeys);
        }
    }


}
